// Forward Compositor
// (c) jimon game studio

#include "CForwardCompositor.h"
#include "ISceneManager.h"
#include "IVariables.h"

namespace je
{
	namespace scene
	{
		//! Constructor
		CForwardCompositor::CForwardCompositor(ISceneManager * SceneManager)
			:ICompositor(SceneManager,"ForwardCompositor")
		{
			MaxLightsPerPass = core::GetVariables()->GetVariablec8("SceneManager_DSCompositor_Lights_PerPass");

			CurrentSceneManager->GetRender()->SetShaderMaterialDefine("SOLID_VS_VARS","FOUT vec3 jeSolidPosition;\\\nuniform mat4 MVMat;");
			CurrentSceneManager->GetRender()->SetShaderMaterialDefine("SOLID_VS_MAIN","jeSolidPosition = (MVMat * IPosition).xyz;");
			CurrentSceneManager->GetRender()->SetShaderMaterialDefine("SOLID_FS_VARS",jeStringc("SIN vec3 jeSolidPosition;\\\nuniform vec3 jeForward_SolidLightPosition[")+jeStringc(MaxLightsPerPass)+jeStringc("];\\\nuniform int jeLightsNumSolid;"));
			CurrentSceneManager->GetRender()->SetShaderMaterialDefine("SOLID_FS_MAIN(__Color,__Normal)","\
				vec4 jeTempResult = vec4(0.0);\\\n\
				for(int i = 0; i < jeLightsNumSolid;i++)\\\n\
				{\\\n\
					vec3 LightDir = normalize(jeForward_SolidLightPosition[i]-jeSolidPosition);\\\n\
					float Diff = max(dot(__Normal,LightDir),0.0);\\\n\
					float Power = 4;\\\n\
					float Dist = clamp(1-distance(jeSolidPosition,jeForward_SolidLightPosition[i])/Power,0.0,1.0);\\\n\
					float Specular = 0.0;\\\n\
					if(Diff > 0.1)\\\n\
						Specular = pow(max(-normalize(reflect(LightDir,__Normal)).z,0.0),200.0);\\\n\
					jeTempResult += (__Color*Diff+vec4(Specular))*Dist;\\\n\
				}\\\n\
				OUTCOLOR0 = jeTempResult;");

			CurrentSceneManager->GetRender()->SetShaderMaterialDefine("TRANSPARENT_VS_VARS","FOUT vec3 jeTransparentPosition;\\\nuniform mat4 MVMat;");
			CurrentSceneManager->GetRender()->SetShaderMaterialDefine("TRANSPARENT_VS_MAIN","jeTransparentPosition = (MVMat * IPosition).xyz;");
			CurrentSceneManager->GetRender()->SetShaderMaterialDefine("TRANSPARENT_FS_VARS",jeStringc("SIN vec3 jeTransparentPosition;\\\nuniform vec3 jeForward_TransparentLightPosition[")+jeStringc(MaxLightsPerPass)+jeStringc("];\\\nuniform int jeLightsNumTransparent;"));
			CurrentSceneManager->GetRender()->SetShaderMaterialDefine("TRANSPARENT_FS_MAIN(__Color,__Normal)","\
				vec3 jeTempResult = vec3(0.0);\\\n\
				for(int i = 0; i < jeLightsNumTransparent;i++)\\\n\
				{\\\n\
					vec3 LightDir = normalize(jeForward_TransparentLightPosition[i]-jeTransparentPosition);\\\n\
					float Diff = max(dot(__Normal,LightDir),0.0);\\\n\
					float Power = 4;\\\n\
					float Dist = clamp(1-distance(jeTransparentPosition,jeForward_TransparentLightPosition[i])/Power,0.0,1.0);\\\n\
					float Specular = 0.0;\\\n\
					if(Diff > 0.1)\\\n\
						Specular = pow(max(-normalize(reflect(LightDir,__Normal)).z,0.0),200.0);\\\n\
					jeTempResult += (__Color.rgb*Diff+vec3(Specular))*Dist;\\\n\
				}\\\n\
				OUTCOLOR0 = vec4(jeTempResult,__Color.a);");

			CurrentSceneManager->GetRender()->RecompileShaders();

			Solid_LightPosition = CurrentSceneManager->GetRender()->GetGlobalShaderVariable("jeForward_SolidLightPosition",true);
			Solid_LightPosition->SetCachedSetup(true);
			Solid_LightPosition->SetIgnoreVoidShaderVariables(true);
			Solid_LightPosition->RemoveVoidShaderVariables();

			Solid_LightPosition_Count = CurrentSceneManager->GetRender()->GetGlobalShaderVariable("jeLightsNumSolid",true);
			Solid_LightPosition_Count->SetCachedSetup(true);
			Solid_LightPosition_Count->SetIgnoreVoidShaderVariables(true);
			Solid_LightPosition_Count->RemoveVoidShaderVariables();

			Transparent_LightPosition = CurrentSceneManager->GetRender()->GetGlobalShaderVariable("jeForward_TransparentLightPosition",true);
			Transparent_LightPosition->SetCachedSetup(false);
			Transparent_LightPosition->SetIgnoreVoidShaderVariables(true);
			Transparent_LightPosition->RemoveVoidShaderVariables();

			Transparent_LightPosition_Count = CurrentSceneManager->GetRender()->GetGlobalShaderVariable("jeLightsNumTransparent",true);
			Transparent_LightPosition_Count->SetCachedSetup(true);
			Transparent_LightPosition_Count->SetIgnoreVoidShaderVariables(true);
			Transparent_LightPosition_Count->RemoveVoidShaderVariables();

			JENEW(CurrentCallback,CTransparentLightPositionSetupCallback(Transparent_LightPosition,Transparent_LightPosition_Count))
		}

		//! Destructor
		CForwardCompositor::~CForwardCompositor()
		{
			CurrentCallback->Drop();
			Transparent_LightPosition_Count->Drop();
			Transparent_LightPosition->Drop();
			Solid_LightPosition_Count->Drop();
			Solid_LightPosition->Drop();
		}

		//! On Render
		void CForwardCompositor::OnRender()
		{
			video::IRender * Render = CurrentSceneManager->GetRender();

			// Setup view transformations
			CurrentSceneManager->GetCurrentCamera()->ApplyTransforms();
			jeMatrix4f CurrentViewMatrix = Render->GetMatrix(video::RMT_VIEW);

			// Setup lights arrays for multipass rendering
			jeDynamicArray<CTransparentLightPositionSetupCallback::LightPositionsArrayType*> LightPositions;
			for(ISceneManager::LightListType::Iterator Iter = CurrentSceneManager->GetLightList()->begin();Iter!=CurrentSceneManager->GetLightList()->end();Iter++)
			{
				if((!LightPositions.GetSize()) || (LightPositions.GetLast()->GetSize() == MaxLightsPerPass))
				{
					CTransparentLightPositionSetupCallback::LightPositionsArrayType * Temp = NULL;
					JENEW(Temp,CTransparentLightPositionSetupCallback::LightPositionsArrayType(MaxLightsPerPass))
					LightPositions.InsertElement(Temp);
				}
				LightPositions.GetLast()->InsertElement(CurrentViewMatrix.MultiplyMatrixOnVector((*Iter)->GetPosition()));
			}

			// Render solid objects
			Render->SetDepthMode();
			Render->SetGlobalMaterialType(false);

			for(u32 LightPassIndex=0;LightPassIndex<LightPositions.GetSize();LightPassIndex++)
			{
				Solid_LightPosition->SetVector(LightPositions[LightPassIndex]->GetPtr(),LightPositions[LightPassIndex]->GetSize());
				Solid_LightPosition_Count->Sets32(LightPositions[LightPassIndex]->GetSize());
				CurrentSceneManager->RenderQueueGroups(false,RPG_SOLID);
				Render->SetDepthMode(video::DM_EQUAL);
				Render->SetGlobalMaterialType(true,video::MT_ADDITIVEBLEND);
			}

			Render->SetDepthMode();
			Render->SetGlobalMaterialType(false);

			// Render objects with no light
			CurrentSceneManager->RenderQueueGroups(false,RPG_SOLID_WITHOUTLIGHTING);

			// Render transparent objects
			Render->SetPreRenderOperationCallback(CurrentCallback);

			for(u32 i=0;i<CurrentSceneManager->GetCountOfObjectsInQueueGroup(scene::RPG_TRANSPARENT);i++)
			{
				Render->SetGlobalMaterialType(false);
				for(u32 LightPassIndex=0;LightPassIndex<LightPositions.GetSize();LightPassIndex++)
				{
					CurrentCallback->SetLightPositions(LightPositions[LightPassIndex]);
					CurrentSceneManager->RenderQueueObject(i,scene::RPG_TRANSPARENT);
					Render->SetGlobalMaterialType(true,video::MT_LIGHTBLEND);
				}
			}

			Render->SetGlobalMaterialType(false);
			Render->SetPreRenderOperationCallback(NULL);

			// Clear light arrays
			for(u32 i=0;i<LightPositions.GetSize();i++)
			{
				LightPositions[i]->Clear();
				JEDELETE(LightPositions[i])
			}
			LightPositions.Clear();

			ICompositor::OnRender();
		}

		//! Serialize
		void CForwardCompositor::Serialize(core::ISerializeStream * SerializeStream)
		{
			ICompositor::Serialize(SerializeStream);
		}
	}
}
